

/*
   led控制器 1903
    主要功能
    1控制led
    2mqtt数据交互
    3集群
*/

//#include "ledc.h"



#define UPDATE  35
#define LEDSHOW 1
#define BLINK 2
#define PAOMA 3
#define BREATHE 4
#define CHANGE_COLOR 5
#define ALL_ON       6
#define ALL_OFF      7
#define LEDTEST      37


#include <HTTPUpdate.h>
uint8_t test_flag = 0;

/*------------------------定义设备相关数据-----------------------------*/
String TID = "104";//测试号
String DEV = "4";//负责局部控制的设备，通过 tid=1 and topic=1 查询 设备库中该设备下包含的关键参数
boolean RGBW = false;//是否是rgbw









//https每次都要阻塞个五秒，对wifi安全性要求高，需要使用安全的wifi库，这里寻卡的时候使用http而不是s，低频，低量
String baseUrl = "http://xf.mianmianshi.com";
/*------------------------定义wifi相关数据-----------------------------*/
#include <WiFiMulti.h>






//1当前固定场所

const char* wifi_name_a = "A幢三楼";
const char* wifi_psd_a = "88888888";

//const char* wifi_name_a = "Tenda_2104C0";
//const char* wifi_psd_a = "lh320724";

//2通用热点
const char* wifi_name_b = "yqwl";
const char* wifi_psd_b = "88888888";



#include <HTTPClient.h>
#include <ArduinoJson.h>//字符串处理
WiFiMulti wifiMulti;     //全局使用

/*------------------------定义mqtt相关数据-----------------------------*/
WiFiClient wfclient;
#include <PubSubClient.h>
PubSubClient client(wfclient);
const char*mqttServer = "47.100.188.31";
const int mqttPort = 1883;
const char*mqttUser = "root";
const char*mqttPassword = "root";
String mqttTopic;
String mqttClientId ;


/*------------------------定义读卡相关数据-----------------------------*/

#define buzzer          18           //蜂鸣器引脚


//#include <SPI.h>
//#include <MFRC522.h>
//#define RST_PIN         22           // Configurable, see typical pin layout above  esp32 https://blog.csdn.net/qq_29338243/article/details/89631227
//#define SS_PIN          21
//
//MFRC522 mfrc522 = MFRC522(SS_PIN, RST_PIN); // Create instance
//MFRC522::MIFARE_Key key;
//byte nuidPICC[4];// 初始化数组用于存储读取到的NUID


/*------------------------定义led相关数据-----------------------------*/
#include <NeoPixelBus.h>
const unsigned int  NUMPIXELS = 300;//0~65536个灯//大数据量时，需配置mqtt传输数据量，我这里先限制在30kb
const uint8_t PixelPin = 16;  // make sure to set this to the correct pin, ignored for Esp8266
byte* ledBytes = NULL;//led缓存数据指针
uint16_t ledBytesLength = 0;
#define colorSaturation 255
#if RGBW
NeoPixelBus<NeoGrbwFeature, NeoEsp32Rmt1800KbpsMethod> pixels(NUMPIXELS, PixelPin);//rgbw
RgbwColor black(0, 0, 0, 0);

void  doLedData(byte * payload, int length) {
  //顺序，isbright,area,alno（2字节）,r,g,b  示例：1 1 4 0 255 0 0 0
  for (int i = 0; i < length; i += 8) {
    uint8_t isbright = int(payload[i]);
    unsigned int alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
    if (isbright == 1) {
      uint8_t area = payload[i + 1];
      uint8_t r = payload[i + 4];
      uint8_t g = payload[i + 5];
      uint8_t b = payload[i + 6];
      uint8_t w = payload[i + 7];
      pixels.SetPixelColor(alno, RgbwColor(r, g, b, w));
    } else {
      pixels.SetPixelColor(alno, black);
    }
  }
  pixels.Show();
}

void  doDataWithColor(byte * payload, uint16_t length, byte * rgb) {
  //顺序，isbright,area,alno（2字节）,r,g,b  示例：1 1 4 0 255 0 0 0
  for (uint16_t i = 0; i < length; i += 8) {
    uint8_t isbright = payload[i];
    unsigned int alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位

    if (isbright == 1) {
      uint8_t area = payload[i + 1];
      pixels.SetPixelColor(alno, RgbwColor(rgb[0], rgb[1], rgb[2], 255));
    } else {
      pixels.SetPixelColor(alno, black);
    }
  }
  pixels.Show();
}


void doBlink(byte* payload, uint16_t ledLength) {

  for (int k = 0; k < 5; k++) {
    for (int i = 0; i < ledLength; i += 8) {
      uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
      pixels.SetPixelColor(alno, black);
    }
    pixels.Show();
    doBeep(30);
    vTaskDelay(300);
    for (int i = 0; i < ledLength; i += 8) {
      uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
      uint8_t r = payload[i + 4];
      uint8_t g = payload[i + 5];
      uint8_t b = payload[i + 6];
      uint8_t w = payload[i + 7];
      pixels.SetPixelColor(alno,  RgbwColor(r, g, b, w));
    }
    pixels.Show();
    vTaskDelay(300);
  }
}

#else
NeoPixelBus<NeoBrgFeature, NeoEsp32Rmt0800KbpsMethod> pixels(NUMPIXELS, PixelPin);//rgb
RgbColor red(colorSaturation, 0, 0);
RgbColor green(0, colorSaturation, 0);
RgbColor blue(0, 0, colorSaturation);
RgbColor white(colorSaturation);
RgbColor black(0);


void  doLedData(byte * payload, unsigned int length) {

  //顺序，isbright,area,alno（2字节）,r,g,b  示例：1 1 4 0 255 0 0 0
  for (uint16_t i = 0; i < length; i += 8) {
    uint8_t isbright = payload[i];
    uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
    if (isbright == 1) {
      uint8_t area = payload[i + 1];
      uint8_t r = payload[i + 4];
      uint8_t g = payload[i + 5];
      uint8_t b = payload[i + 6];
      //uint8_t w = payload[i + 7];
      pixels.SetPixelColor(alno, RgbColor(r, g, b));
    } else {
      pixels.SetPixelColor(alno, black);
    }
  }
  pixels.Show();
}



void  doDataWithColor(byte * payload, uint16_t length, byte * rgb) {
  //顺序，isbright,area,alno（2字节）,r,g,b  示例：1 1 4 0 255 0 0 0
  for (uint16_t i = 0; i < length; i += 8) {
    uint8_t isbright = payload[i];
    unsigned int alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位

    if (isbright == 1) {
      uint8_t area = payload[i + 1];
      pixels.SetPixelColor(alno, RgbColor(rgb[0], rgb[1], rgb[2]));
    } else {
      pixels.SetPixelColor(alno, black);
    }
  }
  pixels.Show();
}


void doBlink(byte* payload, uint16_t ledLength) {

  for (int k = 0; k < 10; k++) {

    for (int i = 0; i < ledLength; i += 8) {
      uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
      uint8_t r = payload[i + 4];
      uint8_t g = payload[i + 5];
      uint8_t b = payload[i + 6];
      // uint8_t w = payload[i + 7];
      pixels.SetPixelColor(alno,  RgbColor(255, 128, 0));//固定用黄灯寻灯


      // pixels.SetPixelColor(alno,  RgbColor(r, g, b));
    }
    pixels.Show();
    vTaskDelay(300);

    for (int i = 0; i < ledLength; i += 8) {
      uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
      pixels.SetPixelColor(alno, black);
    }
    pixels.Show();
    doBeep(30);
    vTaskDelay(300);
  }
  //恢复
  for (int i = 0; i < ledLength; i += 8) {
    uint16_t alno = ((payload[i + 2] & 0xff) << 8) | (payload[i + 3] & 0xff); //字节转10进制数，2字节，高位左移8位
    uint8_t r = payload[i + 4];
    uint8_t g = payload[i + 5];
    uint8_t b = payload[i + 6];
    // uint8_t w = payload[i + 7];
    pixels.SetPixelColor(alno,  RgbColor(r, g, b));
  }
  pixels.Show();


}


#endif




//NeoEsp32Rmt0800KbpsMethod


// define two tasks
void TaskLed( void *pvParameters );
void TaskCard( void *pvParameters );

// the setup function runs once when you press reset or power the board
void setup() {

  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);


  /*---------------------卡--------------------------------*/
  //  SPI.begin();
  //  mfrc522.PCD_Init();
  //  mfrc522.PCD_DumpVersionToSerial();//串口打印版本号



  pinMode(buzzer, OUTPUT);//蜂鸣器




  /*--------------------led初始化---------------------------------*/
  pixels.Begin();
  pixels.Show();//



  /*--------------------wifi---------------------------------*/
  wifiMulti.addAP(wifi_name_a, wifi_psd_a);
  wifiMulti.addAP(wifi_name_b, wifi_psd_b);
  //  wifiMulti.addAP(wifi_name_a, wifi_psd_a);


  //如果联网了就可以继续运行
  while (wifiMulti.run() != WL_CONNECTED) {
    vTaskDelay(500);
    Serial.print(".");
  }


  if (wifiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
  }



  /*--------------------mqtt订阅---------------------------------*/

  // clientId += String(random(0xffff), HEX)


  mqttClientId = TID + "_" + DEV;
  mqttTopic = TID + "_" + DEV;

  Serial.println("Connectedto the WiFi network");
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("Connectingto MQTT...");
    const char* clientId = mqttClientId.c_str();

    if (client.connect(clientId)) {
      Serial.println("connected");
    } else {
      Serial.print("failedwith state ");
      Serial.print(client.state());
      vTaskDelay(1000);
    }
  }
  //根据既定规则生成topic
  const char* topic = mqttTopic.c_str();
  client.subscribe(topic, 1);




  /***********************************************************************/
  // Now set up two tasks to run independently.
  xTaskCreatePinnedToCore(
    TaskLed
    ,  "TaskLed"   // A name just for humans
    ,  1024 * 32 // 看情况给
    ,  NULL
    ,  1  // Priority, with 3 (configMAX_PRIORITIES - 1) being the highest, and 0 being the lowest.
    ,  NULL
    ,  tskNO_AFFINITY); //tskNO_AFFINITY :表示双核运行 0:cpu0,1:cpu1

  xTaskCreatePinnedToCore(
    TaskCard
    ,  "TaskCard"
    ,  1024 * 16 // Stack size
    ,  NULL
    ,  1  // Priority
    ,  NULL
    ,  1);

  // Now the task scheduler, which takes over control of scheduling individual tasks, is automatically started.
  /***********************************************************************/

}

void loop()
{

  //   if(wifiMulti.run() != WL_CONNECTED) {
  //        Serial.println("WiFi not connected!");
  //        vTaskDelay(1000);
  //    }


  // Empty. Things are done in Tasks.
}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/

void TaskLed(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

  /*
    led灯控制程序
  */
  for (;;) // A Task shall never return or exit.
  {
    if (!client.connected()) {//如果没连上
      reconnect();//重连
    }
    client.loop();
  }//for end


}






void TaskCard(void *pvParameters)  // This is a task.
{
  (void) pvParameters;

  /*
    其他程序
  */

  for (;;)
  {

    if (test_flag) {
    //  Serial.println("开启测试");
      led_test();
    } else {
     // Serial.println("关闭测试");
      vTaskDelay(500);
    }

  }
}



/***********************************************************************/


void my_update(String updateURL) {

  WiFiClient client;
  Serial.println("开始升级.......");

  t_httpUpdate_return ret = httpUpdate.update(client, updateURL);


  // Or:
  //t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "file.bin");

  switch (ret) {
    case HTTP_UPDATE_FAILED:
      Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
      break;

    case HTTP_UPDATE_NO_UPDATES:
      Serial.println("HTTP_UPDATE_NO_UPDATES");
      break;

    case HTTP_UPDATE_OK:
      Serial.println("HTTP_UPDATE_OK");
      break;
  }




}



void my_update1(char* url) {

  WiFiClient client;
  Serial.println("开始升级.......");
  //准备升级 绿色
  pixels.ClearTo(green);
  pixels.Show();


  t_httpUpdate_return ret = httpUpdate.update(client, url);
  // Or:
  //t_httpUpdate_return ret = httpUpdate.update(client, "server", 80, "file.bin");

  switch (ret) {
    case HTTP_UPDATE_FAILED:
      Serial.printf("HTTP_UPDATE_FAILED Error (%d): %s\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
      //升级异常 红色
      pixels.ClearTo(red);
      pixels.Show();
      break;

    case HTTP_UPDATE_NO_UPDATES:
      Serial.println("HTTP_UPDATE_NO_UPDATES");


      break;
    case HTTP_UPDATE_OK:
      Serial.println("HTTP_UPDATE_OK");
      break;
  }
}




































/*
  mqtt消息接收回调方法
*/
void callback(char*topic, byte* payload, unsigned int length) {




  Serial.print("Messagearrived in topic: ");
  Serial.println(topic);

  //    Serial.print("Message:");
  //    for (int i = 0; i < length; i++) {
  //      Serial.print((char)payload[i]);
  //      //Serial.print(" ");
  //    }
  //  Serial.println();
  Serial.print(" length: ");
  Serial.println(length);
  //  Serial.println("-------------开始处理数据----------");



  /*

    1指令部分 :具体需要执行什么指令 ，比如 1 led显示 ，2 led灯效,0系统升级， 1个字节 0-255，
    2数据部分：依据具体命令执行具体操作】
    #define UPDATE  35
    #define LED_SHOW 1
    #define BLINK 2
    #define PAOMA 3
    #define BREATHE 4
    #define CHANGE_COLOR 5

    最大数据量限制 63kb(实际数据量在mqtt配置中，目前配置了30kb)
    led最大个数 8191-1

  */

  uint8_t code = payload[length - 1]; //指令下标
  uint16_t dataLength = length - 1;//数据长度



  switch (code) {
    case LEDSHOW:
      Serial.println("LEDSHOW");

      { //添加缓存数据
        free(ledBytes);
        ledBytes = (byte*)malloc(dataLength);
        memcpy(ledBytes, payload, dataLength);
        ledBytesLength = dataLength;
      }
      //这里执行一个分类
      doLedData(payload, dataLength);
      break;
    case BLINK:
      Serial.println("BLINK");
      doBlink(payload, dataLength);
      break;
    case PAOMA:
      Serial.println("PAOMA");
      Serial.println(ledBytes[0]);
      paoma(0, NUMPIXELS, 20); //跑马 耗时长的话，mqtt活跃只有15秒默认，需设置，不然会断开连接导致后续消息重复
      break;

    case BREATHE:
      Serial.println("BREATHE");
      // Serial.println(ledBytes[0]);
      breathingLed(1);  //呼吸灯
      break;
    case CHANGE_COLOR:
      Serial.println("CHANGE_COLOR");
      Serial.println(ledBytes[0]);
      doDataWithColor(ledBytes, ledBytesLength, payload);//利用当前led显示数据缓存执行灯效
      break;

    case ALL_ON:
      all_on(100);
      break;


    case ALL_OFF:
      all_off(100);
      break;

    case LEDTEST:
      Serial.println("LEDTEST");
      test_flag = !test_flag;
      Serial.println(test_flag);

      break;


    case UPDATE:

      //c++可以这样写
      //      String update_url =  "";
      //      for (int i = 1; i < dataLength; i++) {
      //        update_url += (char)payload[i];
      //      }

      //c写法 //把字节数组转成字符串
      char* url = (char*)payload;
      url[dataLength] = '\0';
      Serial.println(url);
      my_update1(url);
      break;








  }




}









void reconnect() {

  // if (wifiMulti.run() == WL_CONNECTED) {
  //
  //      } else {
  //        wifiMulti.run();
  //        vTaskDelay(300);
  //        Serial.print(".");
  //     }

  // Loop until we're reconnected
  while (!client.connected() && (wifiMulti.run() == WL_CONNECTED)) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    const char* clientId = mqttClientId.c_str();
    if (client.connect(clientId)) {
      Serial.println("connected --clientId");
      Serial.println(clientId);

      // Once connected, publish an announcement...
      client.publish("outTopic", "hello world");
      // ... and resubscribe
      const char* topic = mqttTopic.c_str();
      client.subscribe(topic, 1);
      Serial.println("topic------");
      Serial.println(topic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 3 seconds");
      // Wait 5 seconds before retrying
      vTaskDelay(3000);
    }
  }
}





/**
  http请求
  返回类型 String
*/
String my_http(String address ) {
  String  payload = "";
  if (wifiMulti.run() != WL_CONNECTED) {
    Serial.println("WiFi not connected!");
    doBeep(3000);//报警
    while (wifiMulti.run() != WL_CONNECTED) {
      vTaskDelay(500);
      Serial.print(".");
    }
  } else {
    unsigned long time1 = millis();
    HTTPClient http;
    http.begin(address);
    int httpCode = http.GET();
    Serial.println("httpCode------------>");
    Serial.println(httpCode);
    if (httpCode == HTTP_CODE_OK) {
      Serial.println("http getString!");
      payload = http.getString();
    } else {
      // doBeep(1000);//报警
      Serial.println("请求失败！");
    }
    Serial.println("http end!");
    http.end();//注意结构流程，勿忘记关闭资源

    unsigned long time2 = millis();
    Serial.println("time(ms):");
    Serial.println(time2 - time1);
  }
  return payload;
}





/**
  跑马
  给定参数：
  1.起始灯号，0开始
  2.末尾灯号 1开始
  3.间隔时间（单次亮或灭）
*/
void paoma(int first, int last, int ms) {
  for (int i = first; i < last; i++) {
    pixels.SetPixelColor(i, white);
    pixels.Show();
    vTaskDelay(ms);
    pixels.SetPixelColor(i, black);
    pixels.Show();
    vTaskDelay(ms);
  }
}



/**
  蜂鸣器
*/
void doBeep(int mmss) {
  digitalWrite(buzzer, HIGH);
  vTaskDelay(mmss);
  digitalWrite(buzzer, LOW);
  Serial.println("这里响了一下！");
}

/**
  全亮
*/

void all_on(int ms) {
  pixels.ClearTo(white);
  pixels.Show();
  vTaskDelay(ms);
}

/**
  全灭
*/
void all_off(int ms) {
  pixels.ClearTo(black);
  pixels.Show();
  vTaskDelay(ms);
}










/**
   呼吸灯 rgb
*/
void breathingLed(byte n) {

  /*-------------------------------------*/
  for (int i = 0; i <= 255; i += 5) {
    pixels.ClearTo(RgbColor(0, i, i));
    pixels.Show();
    vTaskDelay(40);
  }

  for (int i = 255; i >= 0 ; i -= 5) {
    pixels.ClearTo(RgbColor(0, i, i));
    pixels.Show();
    vTaskDelay(40);
  }
  /*-------------------------------------*/

  /*-------------------------------------*/
  for (int i = 0; i <= 255; i += 5) {
    pixels.ClearTo(RgbColor(i, 0, i));
    pixels.Show();
    vTaskDelay(40);
  }

  for (int i = 255; i >= 0 ; i -= 5) {
    pixels.ClearTo(RgbColor(i, 0, i));
    pixels.Show();
    vTaskDelay(40);
  }
  /*-------------------------------------*/

  /*-------------------------------------*/
  for (int i = 0; i <= 255; i += 5) {
    pixels.ClearTo(RgbColor(i, i, 0));
    pixels.Show();
    vTaskDelay(40);
  }

  for (int i = 255; i >= 0 ; i -= 5) {
    pixels.ClearTo(RgbColor(i, i, 0));
    pixels.Show();
    vTaskDelay(40);
  }
  /*-------------------------------------*/

  /*-------------------------------------*/
  for (int i = 0; i <= 255; i += 5) {
    pixels.ClearTo(RgbColor(i, i, i));
    pixels.Show();
    vTaskDelay(40);
  }

  for (int i = 255; i >= 0 ; i -= 5) {
    pixels.ClearTo(RgbColor(i, i, i));
    pixels.Show();
    vTaskDelay(40);
  }
  /*-------------------------------------*/

}












/**
  高频测试（呼吸灯效）
*/
void led_test() {

  /*-------------------------------------*/
  for (int i = 0; i <= 255; i += 5) {
    pixels.ClearTo(white);
    pixels.Show();
    vTaskDelay(40);
  }

  //  for (int i = 255; i >= 0 ; i -= 5) {
  //    pixels.ClearTo(RgbColor(i, i, i));
  //    pixels.Show();
  //    vTaskDelay(40);
  //  }
  /*-------------------------------------*/

}
